/**
 * Project: TacticaPescaditos
 * File: Player.java
 */
package org.javahispano.javacup.tacticas_aceptadas.Pescaditos.com.chidotronics.javacup;

import org.javahispano.javacup.modelo.ComandoGolpearBalon;
import org.javahispano.javacup.modelo.ComandoIrA;
import org.javahispano.javacup.modelo.Constantes;
import org.javahispano.javacup.modelo.JugadorDetalle;
import org.javahispano.javacup.modelo.Posicion;

import org.javahispano.javacup.tacticas_aceptadas.Pescaditos.com.chidotronics.javacup.fsm.StateMachine;
import org.javahispano.javacup.tacticas_aceptadas.Pescaditos.com.chidotronics.javacup.geometrics.Vector2D;
import org.javahispano.javacup.tacticas_aceptadas.Pescaditos.com.chidotronics.javacup.messages.MessageDispatcher;
import org.javahispano.javacup.tacticas_aceptadas.Pescaditos.com.chidotronics.javacup.messages.Telegram;
import org.javahispano.javacup.tacticas_aceptadas.Pescaditos.com.chidotronics.javacup.messages.Telegram.MessageType;

/**
 * @author Carlos Alberto
 * class: Player
 */
public abstract class Player<E> {
	

	private static final double CONFORTABLE_ZONE = 121;


	public static final double RECEIVING_RANGE = 121;


	private static final double BRAKING_RATE = 0.8;


	private static final double HOT_REGION = Constantes.LARGO_CAMPO_JUEGO/3;


	private static final double MINIMAL_ZONE = 49;
	
	private final double heightControl;


	public enum PLAYER_ROLE{GOAL_KEEPER, ATTACKER, DEFENDER};

	private PLAYER_ROLE playerRole;
	
	protected TacticaPescaditos owner;






	private boolean controlling;
	
	private Vector2D position;

	private Vector2D velocity;

	private SteeringBehaviors steering;


	private Vector2D homePosition;

	protected double distSqToBall;



	private double maxSpeed;

	private Vector2D side;

	private int id;

	private double kickForce;





	private ComandoIrA gotoCommand;
	
	private ComandoGolpearBalon kickCommand;

	
	private Vector2D heading;

	public abstract void handleMessage(Telegram telegram);

	private double maxPassRangeSq;



	private double distToTrajectory;


	private double passFactor;


	private double passForce;

	public Player(TacticaPescaditos tacticaPescaditos, JugadorDetalle detail,
			PLAYER_ROLE playerRole,double heightControl) {
		this.heightControl =heightControl;
		this.owner = tacticaPescaditos;
		
		this.playerRole = playerRole;
		this.position = new Vector2D();
		
		steering = new SteeringBehaviors(tacticaPescaditos,this);
		velocity = new Vector2D();
		
		
		
		this.kickForce = Constantes.getVelocidadRemate(detail.getRemate());
		passFactor = 0.8d;
		passForce = passFactor;
		
		maxSpeed = Constantes.getVelocidad(detail.getVelocidad());
			
		heading = new Vector2D(0,1);
		side = heading.perp();
		this.id = detail.getNumero() -1;

		calculateMaxRange();
		calculateDribblingFactor();

	
	}
	
	private void calculateMaxRange() {
		double speed = this.kickForce;
		maxPassRangeSq = -0.5* speed*speed  / Ball.GROUND_FRICTION ;
		maxPassRangeSq*=maxPassRangeSq;
		
		

		
	}
	
	public abstract void resetStateMachine();

	/**
	 * @return The maximum velocity
	 */
	public double getMaxVelocity() {
		return maxSpeed;
	}


	public void setHomePosition(Vector2D position) {
		this.homePosition = position;
	}

	public int getId() {
		return id;
	}

	/**
	 * @return the playerRole
	 */
	public PLAYER_ROLE getPlayerRole() {
		return playerRole;
	}


	/**
	 * @return the current Position
	 */
	public Vector2D getPosition() {
		return position;
	}

	/**
	 * @return the velocity as a vector
	 */
	public Vector2D getVelocity() {
		return velocity;
	}

	/**
	 * @return the steering behavior
	 */
	public SteeringBehaviors getSteering() {
		return steering;
	}
	

	/**
	 * @return the homePosition
	 */
	public Vector2D getHomePosition() {
		return homePosition;
	}



	public void update() {
		this.kickCommand = null;
		getStateMachine().update();
		
		
		steering.calculate();

		Vector2D force = steering.getForce();

		if (force.isZero()) {
			velocity = velocity.multiply( BRAKING_RATE );    
		} else {
			velocity = force;
			heading = force.normalized();
			side = heading.perp();	
		}

		Vector2D futurePosition = position.add(velocity);
		
		
		
		try {
			
				this.gotoCommand =  new ComandoIrA(this.id,new Posicion(futurePosition.x,futurePosition.y));
			
		} catch (Exception e) {
		e.printStackTrace();
		}


	}




	/**
	 * @param position the position to set
	 */
	public void setPosition(double x, double y) {
		this.position.setLocation(x, y);
	}



	public boolean isAtTarget() {

		return position.equals(steering.getTarget());
	}



	

	/**
	 * @param velocity the velocity to set
	 */
	public void setVelocity(Vector2D velocity) {
		this.velocity = velocity;
	}



	/**
	 * @param controlling the controlling to set
	 */
	public void setControlling(boolean controlling) {
		this.controlling = controlling;
	}



	/**
	 * @return the controlling
	 */
	public boolean isControlling() {
		return controlling;
	}



	public TacticaPescaditos getTactica() {
		return owner;
	}




	public Vector2D getSide() {
		return side;
	}



	public Vector2D getHeading() {
		return heading;
	}





	public Ball getBall() {
		return owner.getBall();
	}
	
	private double dribblingFactor;


	private boolean _canKick;
	
	private void calculateDribblingFactor(){
		double vB = this.maxSpeed - 0.5*Ball.GROUND_FRICTION*(Constantes.ITERACIONES_GOLPEAR_BALON +1);
		dribblingFactor = vB / this.getKickForce();
	}
	



	public void kick( Posicion destination, double power, double vAngle) {
	
		this.kickCommand = new ComandoGolpearBalon(this.id,destination,power, false);
	}



	public void findSupport() {

		TeamPlayer bestSupportPly = owner.determineBestSupportingAttacker();
		MessageDispatcher dispatcher =  owner.getDispatcher();

		if (owner.getSupportingPlayer() == null){
			owner.setSupportingPlayer(bestSupportPly);
			dispatcher.dispatch(this.id, bestSupportPly.getId(), MessageType.HELP_ME,null,0);
		}

		bestSupportPly = owner.determineBestSupportingAttacker();

		TeamPlayer supportingPlayer = owner.getSupportingPlayer();
		if (bestSupportPly != null && (bestSupportPly != supportingPlayer)){

			if (supportingPlayer != null){
				dispatcher.dispatch(this.id, bestSupportPly.getId(), MessageType.WAIT,null,0);
			}

			owner.setSupportingPlayer(bestSupportPly);
			dispatcher.dispatch(this.id, bestSupportPly.getId(), MessageType.HELP_ME,null,0);
		}
	}



	public boolean isThreatened() {

		 for (Vector2D position :owner.getOpponentPositions()){
			 double distanceSq = this.position.distanceSq( position);
			 if (distanceSq<MINIMAL_ZONE ||( inFrontOfMe(position) && 
					 ( distanceSq < CONFORTABLE_ZONE))) {        
				      return true;
				    
			 }
		 }
		return false;
	}



	private boolean inFrontOfMe(Vector2D pos) {
		  return (this.position.substract(pos) .dot(getHeading()) > 0) ;
		    
		 
	}



	public boolean inHotRegion() {
		  return  position.y > HOT_REGION;   
	}



	public boolean ballWithinReceivingRange() {
		return  distSqToBall <= RECEIVING_RANGE;
	}











	/**
	 * @return the command
	 */
	public ComandoGolpearBalon getKickCommand() {
		return this.kickCommand;
	}



	/**
	 * @return the kickEnabled
	 */
	public boolean canKick() {
		return  _canKick && this.kickCommand == null;
	}






	/**
	 * @return the maxPassRange squared
	 */
	public double getMaxPassRangeSq() {
		return maxPassRangeSq;
	}


	
	public boolean isAheadAttacker() {
		Player<?> controllingPlayer = owner.getControllingPlayer();
		
		double meToGoal = Math.abs(Constantes.centroArcoSup.getY() - this.position.getY() );
		double controllerToGoal = Math.abs(Constantes.centroArcoSup.getY()
									- controllingPlayer.getPosition().getY() );
		return meToGoal < controllerToGoal;
	}


	public boolean isClosestToBall() {
		return getTactica().getPlayerClosestToBall() == this;
	}
	
	public boolean isClosestToTrajectory() {
		
		return this == owner.getPlayerClosestToTrajectory();
	}




	public void setDistSqToBall(double dist) {
		this.distSqToBall = dist;
		
	}

	public abstract StateMachine<E> getStateMachine() ;

	/**
	 * @return the kickForce
	 */
	public double getKickForce() {
		return kickForce;
	}
	
	/**
	 * @return the passForce
	 */
	public double getPassForce() {
		return passForce;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		return this.id;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return String.valueOf(this.id+1);
	}



	/**
	 * @return the gotoCommand
	 */
	public ComandoIrA getGotoCommand() {
		return gotoCommand;
	}

	/**
	 * @return the heightControl
	 */
	public double getHeightControl() {
		return heightControl;
	}



	/**
	 * @param distToTrajectory
	 */
	public void setDistSqToTrajectory(double distToTrajectory) {
		this.distToTrajectory = distToTrajectory;
		
	}

	/**
	 * @return the distSqToBall
	 */
	public double getDistSqToBall() {
		return distSqToBall;
	}

	/**
	 * @return the distToTrajectory
	 */
	public double getDistToTrajectory() {
		return distToTrajectory;
	}

	/**
	 * @return the dribblingFactor
	 */
	public double getDribblingFactor() {
		return dribblingFactor;
	}

	/**
	 * @param kick the _canKick to set
	 */
	public void setCanKick(boolean kick) {
		_canKick = kick;
	}

	/**
	 * @return the passFactor
	 */
	public double getPassFactor() {
		return passFactor;
	}





}
